Message.svelte 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. <script>
  2. let { turn, role, streaming = false } = $props();
  3. const isUser = role === "user";
  4. const content = $derived(isUser ? turn.user_text : turn.llm_text);
  5. function formatDateTime(dateStr) {
  6. return new Date(dateStr).toLocaleString("en-US", {
  7. month: "short",
  8. day: "numeric",
  9. hour: "numeric",
  10. minute: "2-digit",
  11. hour12: true
  12. });
  13. }
  14. </script>
  15. <div class="message" class:user={isUser} class:assistant={!isUser}>
  16. <div class="bubble">
  17. <p class="text">
  18. {content}{#if streaming}<span class="cursor"></span>{/if}
  19. </p>
  20. {#if !streaming}
  21. <div class="meta">
  22. <span class="time">{formatDateTime(turn.created_at)}</span>
  23. {#if !isUser}
  24. <span class="dot">·</span>
  25. <span class="tokens">{turn.tokens_used} tokens</span>
  26. {/if}
  27. </div>
  28. {/if}
  29. </div>
  30. </div>
  31. <style>
  32. .message {
  33. display: flex;
  34. max-width: 100%;
  35. }
  36. .message.user {
  37. justify-content: flex-end;
  38. }
  39. .message.assistant {
  40. justify-content: flex-start;
  41. }
  42. .bubble {
  43. max-width: 72%;
  44. display: flex;
  45. flex-direction: column;
  46. gap: 0.4rem;
  47. }
  48. .message.user .bubble {
  49. align-items: flex-end;
  50. }
  51. .message.assistant .bubble {
  52. align-items: flex-start;
  53. }
  54. .text {
  55. padding: 0.75rem 1rem;
  56. border-radius: 16px;
  57. font-size: 0.95rem;
  58. line-height: 1.6;
  59. color: #e5e5ea;
  60. white-space: pre-wrap;
  61. word-break: break-word;
  62. }
  63. .message.user .text {
  64. background: #5a5aff;
  65. border-bottom-right-radius: 4px;
  66. }
  67. .message.assistant .text {
  68. background: #1a1a28;
  69. border: 1px solid #2a2a3a;
  70. border-bottom-left-radius: 4px;
  71. }
  72. .meta {
  73. display: flex;
  74. align-items: center;
  75. gap: 0.35rem;
  76. font-size: 0.72rem;
  77. color: #44445a;
  78. padding: 0 0.25rem;
  79. }
  80. .dot {
  81. color: #2a2a3a;
  82. }
  83. .cursor {
  84. display: inline-block;
  85. width: 2px;
  86. height: 1em;
  87. background: #a855f7;
  88. margin-left: 2px;
  89. vertical-align: text-bottom;
  90. border-radius: 1px;
  91. animation: blink 0.9s ease-in-out infinite;
  92. }
  93. @keyframes blink {
  94. 0%, 100% { opacity: 1; }
  95. 50% { opacity: 0; }
  96. }
  97. @media (max-width: 600px) {
  98. .bubble { max-width: 88%; }
  99. }
  100. </style>